Jx.categorize('code.url.tokenizer', {

	$extend: 'Jx.lib.Tokenizer',

	states: {

		'start': {

			':': 'colon',
			'@': 'at',
			'/': 'slash',
			'%': 'escape',
			'[*]': 'literal'

		},

		'colon': {

			'/': 'scheme_separator1'

		},

		'scheme_separator1': {

			'/': 'scheme_separator'

		},

		'escape': {

			'0': 'escape_1',
			'1': 'escape_1',
			'2': 'escape_1',
			'3': 'escape_1',
			'4': 'escape_1',
			'5': 'escape_1',
			'6': 'escape_1',
			'7': 'escape_1',
			'8': 'escape_1',
			'9': 'escape_1',
			'a': 'escape_1',
			'b': 'escape_1',
			'c': 'escape_1',
			'd': 'escape_1',
			'e': 'escape_1',
			'f': 'escape_1',
			'A': 'escape_1',
			'B': 'escape_1',
			'C': 'escape_1',
			'D': 'escape_1',
			'E': 'escape_1',
			'F': 'escape_1'

		},

		'escape_1': {

			'0': 'escaped',
			'1': 'escaped',
			'2': 'escaped',
			'3': 'escaped',
			'4': 'escaped',
			'5': 'escaped',
			'6': 'escaped',
			'7': 'escaped',
			'8': 'escaped',
			'9': 'escaped',
			'a': 'escaped',
			'b': 'escaped',
			'c': 'escaped',
			'd': 'escaped',
			'e': 'escaped',
			'f': 'escaped',
			'A': 'escaped',
			'B': 'escaped',
			'C': 'escaped',
			'D': 'escaped',
			'E': 'escaped',
			'F': 'escaped'

		},

		'literal': {},

		'escaped': {},

		'scheme_separator': {},

		'at': {},

		'slash': {}

	},

	stateCallback: {

		'colon': function(chr, input, state, previousState){

			this.appendBuffer(chr);

			return 'colon';

		},

		'scheme_separator': function(chr, input, state, previousState){

			this.appendBuffer(chr);

			return 'scheme_separator';

		},

		'escape': function(chr, input, state, previousState){

			this.escape_buffer = '';

		},

		'escape_1': function(chr, input, state, previousState){

			this.escape_buffer += chr;

		},

		'escaped': function(chr, input, state, previousState){

			this.escape_buffer += chr;

			this.appendBuffer(

				String.fromCharCode(

					parseInt(this.escape_buffer, 16)

				)

			);

			this.escape_buffer = '';

			this.state = 'literal';

		},

		'literal': function(chr, input, state, previousState){

			this.appendBuffer(chr);

			return 'literal';

		}

	},

	escape_buffer: ''

});
